
/****************************************************************************
**
** Copyright (C) 2004-2008 VATATA.com . All rights reserved.
**
** This file is the part of the Miniutil Poject of VATATA.com.
** Vatata's miniutil library is lightweight and portable (for linux
** and Windows). The C++ Class Library included Threadpool, Dll danymic loading, 
** General socket application model and tcp server, httpserver/servlet, etc. 
** It is a pratical utility disigned for who don't want learn the Big C++ 
** Developing Platform, such as  Boost or ACE.
**
** This file may be used under the terms of the GNU General Public
** License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file.  Alternatively you may (at
** your option) use any later version of the GNU General Public
** License if such license has been publicly approved by VATATA.com.
** (or its successors, if any) and the Miniutil Project Aministrators. 
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://www.gnu.org/licenses/lgpl.html. If you are unsure which 
** license is appropriate for your use, please review the following 
** information: http://code.google.com/p/miniutil/ or contact 
** http://www.vatata.com.
**
** In addition, as a special exception, VATATA.com, as the sole
** copyright holder for Miniutil Project, grants users of VATATA
** P2P Platform the right to release and package with the related 
** libraries without the source code.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. vatata reserves all rights not expressly
** granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/



#include "miniutil/process.h"
#include "miniutil/diru.h"
#include "miniutil/miniconv.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef WIN32

#include <sys/types.h> 
#include <sys/wait.h> 
#include <errno.h>
#include <signal.h>

#endif

using namespace miniutil;

process::process( string cmd ) :
			__errorCode( UNKNOWN_ERROR ),
			__exitCode(0),
			runningFlag(false)
{
#ifdef WIN32
	HANDLE chIn, chOut, chErr;
	SECURITY_ATTRIBUTES   sa;  
	sa.bInheritHandle   =   true;  
	sa.lpSecurityDescriptor   =   NULL;  
	sa.nLength   =   sizeof(sa);  

	this->__errorCode = CREATEPIPE_FAILED;

	if(   !CreatePipe(&in,  &chOut,  &sa, 0) ) 
		return;
	
	if(   !CreatePipe(&out, &chIn,   &sa, 0) )
	{
		::CloseHandle(in);
		::CloseHandle(chOut);
		return;
	}
	if(   !CreatePipe(&err, &chErr,  &sa, 0) ) 
	{
		::CloseHandle(in);
		::CloseHandle(chOut);
		::CloseHandle(out);
		::CloseHandle(chIn);
		return;
	}

	ZeroMemory(&this->childInfo, sizeof(PROCESS_INFORMATION));
	STARTUPINFO si;  
	ZeroMemory( &si,   sizeof(si));  

	si.cb   =   sizeof(STARTUPINFO);  
	si.dwFlags   =     STARTF_USESTDHANDLES   |   STARTF_USESHOWWINDOW;  
	si.wShowWindow   =   SW_HIDE; // SW_SHOW; //
	si.hStdInput   =   chIn;   //ضӽ  
	si.hStdOutput   =  chOut;   //ضӽ  
	si.hStdError   =   chErr;  //GetStdHandle(   STD_ERROR_HANDLE   );

	this->__errorCode = CREATEPROC_FAILED;
	if(   !::CreateProcess(NULL, (LPSTR) cmd.c_str(),   NULL,   NULL,   true,   0,  NULL,   NULL,  &si,  &this->childInfo)  )  
	{  
		::CloseHandle(in);
		::CloseHandle(chOut);
		::CloseHandle(out);
		::CloseHandle(chIn);
		::CloseHandle(err);
		::CloseHandle(chErr);
		return ;
	}   

	::CloseHandle(   chIn  );   
	::CloseHandle(   chOut );
	::CloseHandle(   chErr );

#else
	int ins[2], outs[2], errs[2];
	pid_t pid;

	this->__errorCode = CREATEPIPE_FAILED;
	if ( pipe(ins) != 0 || pipe( outs ) != 0 || pipe( errs ) != 0 )
		return;
	// ûд?

	this->childInfo = fork();
	if ( this->childInfo == 0 )
	{
		// ӽ
		dup2(outs[1], fileno(stdout));
		dup2(errs[1], fileno(stderr));
		dup2(ins[0],  fileno(stdin));

		string cmdline(cmd);
		char * cmdbuf = (char *)cmdline.c_str();
		int count = 0;
		char *p = cmdbuf;
		while(  *p != 0 )
		{
			if ( *p == ' ' || *p == '\t' )
				count++;
			p++;
		}

		char **argv = (char **) malloc( sizeof(char *) * (count + 1 ) );
		memset( argv, 0, sizeof(char *) * (count + 1 ) );

		int n =0;
		for( p= cmdbuf; *p != 0 && n < count+1 ; p++ )
		{
			if ( *p == ' ' || *p == '\t' )
				*p = '\0';
			else if ( (p) <= cmdbuf || ( *(p-1) == '\0' && *p != '\0' ) )
			{
				argv[n++] = p;
			}
		}

		execvp( argv[0], argv );
		//TODO argvĹϵ
		fprintf(stderr, "can not exec");
	}
	else
	{
		in  = outs[0];
		out = ins[1];
		err = errs[0];
		close( outs[1] );
		close( ins[0] );
		close( errs[1] );
	}

#endif

	this->__errorCode = PROCESS_OK ;
	runningFlag = true;
}

process::~process()
{
	if ( this->isRunning() )
	{
		this->killProcess();
	}
}

process * process::create( string cmd )
{
	process * p = new process( cmd );
	if ( p->runningFlag == true )
		return p;
	delete p;
	return NULL;
}

bool process::isRunning()
{
	//TODO child processǷȷлѾ𣬼ӦchildProc.hThread߲sig_pipeźţ
	if ( this->runningFlag )
	{
#ifdef WIN32
		DWORD exitcode;
		if ( GetExitCodeProcess(childInfo.hProcess, &exitcode) && exitcode == STILL_ACTIVE )
			return true;
		this->__exitCode = exitcode;
		::CloseHandle( in );
		::CloseHandle( out );
		::CloseHandle( err );
#else
		if (waitpid(childInfo, &__exitCode, WNOHANG) <= 0)
			return true;
		close(in);
		close(out);
		close(err);
#endif

	}
	__exitCode = __exitCode & 0xff;

	this->runningFlag = false;
	return false;
}

int process::waitProcess()
{
	if ( this->isRunning() == false )
		return this->__exitCode;

#ifdef WIN32
	WaitForSingleObject( this->childInfo.hProcess, INFINITE);
#else
	waitpid(childInfo, &this->__errorCode, 0);
	// TODO: error handling
#endif

	return __exitCode;
}

void process::killProcess()
{
	if ( this->isRunning() == false )
		return;

#ifdef WIN32
	TerminateProcess(childInfo.hProcess, -1);
	CloseHandle(childInfo.hProcess);
	CloseHandle(childInfo.hThread);
	childInfo.hProcess = INVALID_HANDLE_VALUE;
	childInfo.hThread = INVALID_HANDLE_VALUE;
#else
	kill(childInfo, SIGKILL);
#endif

	this->runningFlag = false;
}

int process::getExitCode()
{
	if ( this->isRunning() )
		return 0;
	return this->__exitCode;
}

long process::read( char *buf, long size )
{
	if ( this->isRunning() == false )
		return -1;

#ifdef WIN32
	DWORD l = (DWORD) size;
	if ( ! ReadFile( in,  buf, size, &l ,NULL ) )
	{
		if ( this->isRunning() == false )
			return -1;
		return -1;
	}
	return (long)l;
#else
	int l = ::read( in, buf, (unsigned int) size );
	if ( l <= 0 )
	{
		if (errno == EAGAIN || errno == ENOENT || errno == EINTR )
			return 0;
		if ( this->isRunning() == false )
			return -1;
		return -1;
	}
	return l;

#endif
}

long process::readerr( char *buf, long size )
{
	if ( this->isRunning() == false )
		return -1;

#ifdef WIN32
	DWORD l = (DWORD) size;
	if ( ! ReadFile( err,  buf, size, &l ,NULL ) )
	{
		if ( this->isRunning() == false )
			return -1;
		return -1;
	}
	return (long)l;
#else
	int l = ::read( err, buf, (unsigned int) size );
	if ( l <= 0 )
	{
		if (errno == EAGAIN || errno == ENOENT || errno == EINTR )
			return 0;
		if ( this->isRunning() == false )
			return -1;
		return -1;
	}
	return l;

#endif
}

long process::write(char *buf, long size )
{
	if ( this->isRunning() == false )
		return -1;

#ifdef WIN32
	DWORD w;
	if (!WriteFile( out, buf, size, &w, NULL))
	{
		if ( this->isRunning() == false )
			return -1;
		return -1;
	}
	return (long)w;
#else
	int w = ::write( out, buf, (unsigned int)size );
	if ( w < 0 )
	{
		if ( this->isRunning() == false )
			return -1;
		return -1;
	}
	return w;
#endif
}

void process::restartSelf( int seconds, bool bshow)
{
	char buf[1024];
#ifdef WIN32
	// todo , windows 98 and before
    sprintf(buf, "/C ping -n %d 127.0.0.1 > nul && %s %s", seconds, miniutil::diru::getModuleName().c_str(), "");
    std::wstring ws = miniutil::MiniConv::String_Utf8_To_UCS2(buf);
	//M_DEBUG( "Exec : cmd %s", buf );
    ::ShellExecuteW( NULL, L"open", L"cmd.exe", ws.c_str(), NULL, bshow ? SW_SHOW : SW_HIDE );
#else
	if ( fork() )
	{
		::exit(0);
	}
	int maxfd= dup(0);
	for(int f=3; f<=maxfd; f++ )
	{
		close(f);
	}
	sprintf(buf, "nohup sh -c 'sleep %d &&  ( %s %s > /dev/null & ) ' & ",  
        seconds, miniutil::diru::getModuleName().c_str(), "");
	//M_DEBUG( "Exec : cmd %s", buf );
	::system( buf );
#endif
}

